package com.aloha.springframework.context.event;

import com.aloha.springframework.beans.BeansException;
import com.aloha.springframework.beans.factory.BeanFactory;
import com.aloha.springframework.beans.factory.BeanFactoryAware;
import com.aloha.springframework.context.ApplicationEvent;
import com.aloha.springframework.context.ApplicationListener;
import com.aloha.springframework.utils.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author DaiZhiHeng
 * @date 2023/7/3 22:55
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void addApplicationListener(ApplicationListener<? extends ApplicationEvent> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<? extends ApplicationEvent> listener) {
        applicationListeners.remove(listener);
    }

    @SuppressWarnings("rawtypes")
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        return applicationListeners.stream().filter(listener -> supportsEvent(listener, event)).collect(Collectors.toCollection(LinkedList::new));
    }

    /**
     * 监听器是否对该事件感兴趣
     */
    protected boolean supportsEvent(ApplicationListener<? extends ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<?> listenerClass = applicationListener.getClass();

        // 根据创建对象的策略选择
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String eventClassName = actualTypeArgument.getTypeName();
        Class<?> eventClass;
        try {
            eventClass = Class.forName(eventClassName);
        } catch (ClassNotFoundException e) {
            throw new BeansException("wrong event class name: " + eventClassName);
        }
        return eventClass.isAssignableFrom(event.getClass());
    }

}
